﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

namespace Utils
{
    /// <summary>
    /// 模型验证
    /// </summary>
    public static class VerifyUtil
    {
        /// <summary>
        /// 创建一个验证模型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dto"></param>
        /// <returns></returns>
        public static Verify<T> Verify<T>(T dto) where T : class, new()
        {
            return new Verify<T>(dto);
        }

        public static VerifyList<T> VerifyList<T>(IList<T> list) where T : class, new()
        {
            return new VerifyList<T>(list);
        }
    }
    public class VerifyList<T>
    {
        private IList<T> selfList;

        private readonly List<Func<T, (string name, string message)>> matchs = new List<Func<T, (string name, string message)>>();

        private readonly Dictionary<string, string> errors = new Dictionary<string, string>();

        public VerifyList(IList<T> dto)
        {
            if (dto == null)
            {
                throw Error.DefaultError("缺少验证对象");
            }
            selfList = dto;
        }

        private string GetDisplayName(MemberInfo info)
        {
            var displayName = info.GetCustomAttribute<System.ComponentModel.DisplayNameAttribute>();
            return displayName == null ? info.Name : displayName.DisplayName;
        }

        /// <summary>
        /// 非空验证
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="selector"></param>
        /// <param name="message"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public VerifyList<T> Required<TKey>(Expression<Func<T, TKey>> selector, string message = "{0}不能为空", Func<T, bool> condition = null)
        {
            return IsMatch(selector, self =>
            {
                var value = selector.Compile().Invoke(self);
                if (value == null)
                {
                    return true;
                }
                else if (value is string)
                {
                    if (string.IsNullOrWhiteSpace(value as string))
                    {
                        return true;
                    }
                }
                else if (value.Equals(default(TKey)))
                {
                    return true;
                }
                else if (value is IEnumerable ie && !ie.GetEnumerator().MoveNext())
                {
                    return true;
                }
                return false;
            }, message, condition);
        }

        /// <summary>
        /// 延迟执行
        /// </summary>
        /// <param name="selector"></param>
        /// <param name="check"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public VerifyList<T> IsMatch<KEY>(Expression<Func<T, KEY>> selector, Func<T, bool> check, string message = "{0}有误", Func<T, bool> condition = null)
        {
            MemberInfo info = (selector.Body as MemberExpression).Member;
            matchs.Add(self =>
            {
                if (condition != null && !condition(self))
                {
                    return (info.Name, "");
                }
                if (check(self))
                {
                    return (info.Name, message.FormatStr(GetDisplayName(info)));
                }
                return (info.Name, "");
            });
            return this;
        }
        /// <summary>
        /// 枚举检查
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="selector"></param>
        /// <param name="check"></param>
        /// <param name="message"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public VerifyList<T> IsDefined<E>(Expression<Func<T, int>> selector, string message = "{0}在枚举中不存在", Func<T, bool> condition = null) where E : Enum
        {
            return IsMatch(selector, self =>
            {
                var value = selector.Compile().Invoke(self);
                return !EnumUtil.IsDefined<E>(value);
            }, message, condition);
        }

        /// <summary>
        /// 正则判断
        /// </summary>
        public VerifyList<T> IsMatch(Expression<Func<T, string>> selector, Regex regex, string message = "{0}有误", Func<T, bool> condition = null)
        {
            return IsMatch(selector, self =>
            {
                var value = selector.Compile().Invoke(self);
                return !regex.IsMatch(value.ToString());
            }, message, condition);
        }

        public void ThrowInputError()
        {
            for (int i = 0; i < selfList.Count; i++)
            {
                foreach (var item in matchs)
                {
                    var (name, message) = item.Invoke(selfList[i]);
                    if (!errors.ContainsKey("0." + name) && !message.IsNullOrEmpty())
                    {
                        errors.Add("0." + name, message);
                    }
                }
            }
            if (errors.Count > 0)
            {
                throw Error.ArgumentsError(errors);
            }
        }

    }

    public class Verify<T>
    {
        private readonly T self;

        private readonly Dictionary<string, string> matchs = new Dictionary<string, string>();

        public Verify(T dto)
        {
            if (dto == null)
            {
                throw Error.DefaultError("缺少验证对象");
            }
            self = dto;
        }

        private string GetDisplayName(MemberInfo info)
        {
            var displayName = info.GetCustomAttribute<DisplayNameAttribute>();
            return displayName == null ? info.Name : displayName.DisplayName;
        }

        public Verify<T> ValidateObject()
        {
            foreach (var item in self.GetType().GetProperties())
            {
                var validates = item.GetCustomAttributes<ValidationAttribute>();
                if (validates != null && validates.Count() > 0)
                {
                    try
                    {

                        foreach (var validate in validates)
                        {
                            validate.Validate(item.GetValue(self), GetDisplayName(item));
                        }
                    }
                    catch (ValidationException ve)
                    {
                        matchs.Add(item.Name, ve.ValidationResult.ErrorMessage ?? ve.Message);
                    }
                }
            }
            return this;
        }

        /// <summary>
        /// 非空验证
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <param name="selector"></param>
        /// <param name="message"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public Verify<T> Required<TKey>(Expression<Func<T, TKey>> selector, string message = null, bool condition = true)
        {
            if (!condition)
            {
                return this;
            }
            MemberInfo info = (selector.Body as MemberExpression).Member;
            if (matchs.ContainsKey(info.Name)) return this;
            var value = selector.Compile().Invoke(self);
            if (value == null)
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}不能为空");
            }
            else if (value is string)
            {
                if (string.IsNullOrWhiteSpace(value as string))
                {
                    matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}不能为空");
                }
            }
            else if (value.Equals(default(TKey)))
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}不能为空");
            }
            else if (value is IEnumerable ie && !ie.GetEnumerator().MoveNext())
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}不能为空");
            }
            return this;
        }

        /// <summary>
        /// 延迟执行
        /// </summary>
        /// <param name="selector"></param>
        /// <param name="check"></param>
        /// <param name="message"></param>
        /// <returns></returns>
        public Verify<T> IsMatch<KEY>(Expression<Func<T, KEY>> selector, Func<bool> check, string message = null, bool condition = true)
        {
            if (!condition)
            {
                return this;
            }
            MemberInfo info = (selector.Body as MemberExpression).Member;
            if (matchs.ContainsKey(info.Name)) return this;
            if (check())
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}有误");
            }
            return this;
        }

        public Verify<T> IsDefined<E>(Expression<Func<T, int>> selector, string message = null, bool condition = true) where E : Enum
        {
            if (!condition)
            {
                return this;
            }
            MemberInfo info = (selector.Body as MemberExpression).Member;
            if (matchs.ContainsKey(info.Name)) return this;
            var value = selector.Compile().Invoke(self);
            if (!EnumUtil.IsDefined<E>(value))
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}在枚举中不存在");
            }
            return this;
        }
        /// <summary>
        /// 枚举检查
        /// </summary>
        /// <typeparam name="E"></typeparam>
        /// <param name="selector"></param>
        /// <param name="check"></param>
        /// <param name="message"></param>
        /// <param name="condition"></param>
        /// <returns></returns>
        public Verify<T> IsDefined<E>(Expression<Func<T, string>> selector, string message = null, bool condition = true) where E : Enum
        {
            if (!condition)
            {
                return this;
            }
            MemberInfo info = (selector.Body as MemberExpression).Member;
            if (matchs.ContainsKey(info.Name)) return this;
            var value = selector.Compile().Invoke(self);
            if (!Enum.IsDefined(typeof(E), value))
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}在枚举中不存在");
            }
            return this;
        }

        /// <summary>
        /// 正则判断
        /// </summary>
        /// <param name="selector"></param>
        /// <param name="pattern"></param>
        /// <param name="message"></param>
        /// <param name="options"></param>
        /// <returns></returns>
        public Verify<T> IsMatch(Expression<Func<T, string>> selector, Regex regex, string message = null, bool condition = true)
        {
            if (!condition)
            {
                return this;
            }
            MemberInfo info = (selector.Body as MemberExpression).Member;
            if (matchs.ContainsKey(info.Name)) return this;
            var value = selector.Compile().Invoke(self);
            if (!regex.IsMatch(value.ToString()))
            {
                matchs.Add(info.Name, message ?? $"{GetDisplayName(info)}有误");
            }
            return this;
        }

        public void ThrowInputError()
        {
            if (matchs.Count > 0)
            {
                throw Error.ArgumentsError(matchs);
            }
        }

    }
}
